import sysRouter from '@ohos.router';
import { AsyncCallback, BusinessError, Callback } from '@ohos.base';
import bundleManager from '@ohos.bundle.bundleManager';
import hilog from '@ohos.hilog';
import { Ability, UIAbility, AbilityConstant, Want, common } from '@kit.AbilityKit';
import observer from '@ohos.arkui.observer'
import * as utils from './utils'
import { UIContext } from '@ohos.arkui.UIContext';

/**
 * 路由记录装饰器工厂，用于配置页面的路由信息
 * @param options 路由记录选项
 * @returns ClassDecorator
 */
const RouteDecorator = function (_options?: router.RouteOption): ClassDecorator {
  return (_target: Object) => {
    // ignore
  }
}

/**
 * 路由记录装饰器工厂，用于配置页面的路由信息
 */
export const Route = RouteDecorator

const RouterStoragePropName: string = '__EASY_ROUTER_STORAGE__'

/**
 * 增强路由组件
 */
namespace router {
  /**
   * 路由记录装饰器工厂，用于配置页面的路由信息
   */
  export const Route = RouteDecorator

  /**
   * 增强路由装饰器工厂，用于配置UIAbility以便自动初始化路由组件
   * @param options 增强选项
   * @returns ClassDecorator
   */
  export function EnhanceRouter(options?: RoutingOptions): ClassDecorator {
    return (target: Object) => {
      if (target && target instanceof Function && isAbility(target)) {
        let oldOnCreate = target.prototype.onCreate
        target.prototype.onCreate = function (want: Want, launchParam: AbilityConstant.LaunchParam): void {
          router.init(this.context, options)
          oldOnCreate && oldOnCreate.apply(this, [want, launchParam])
        }
        let oldOnDestroy = target.prototype.onDestroy
        target.prototype.onDestroy = function (): void {
          VarStorage.onDestroy()
          oldOnDestroy && oldOnDestroy.apply(this, [])
        }
      }
    }
  }

  /**
   * 初始化路由组件
   * @param context 上下文
   * @param options 路由选项
   */
  export function init(context: common.UIAbilityContext, options?: RoutingOptions): void {
    let vs = VarStorage.getInstance()
    if (vs.context) {
      return
    }
    vs.context = context
    vs.routingOptions = options || {}
    vs.routingOptions.rewriteMode = vs.routingOptions.rewriteMode || RewriteMode.Auto
    vs.pageManaged = options?.pageManaged
    tryInit()
  }

  export function back(options?: number | RouterOptions, params?: Object): void {
    if (options) {
      if (isRouterOptions(options)) {
        options = normalize(options) as RouterOptions
        sysRouter.back(options)
      } else {
        if (params) {
          sysRouter.back(options, params)
        } else {
          sysRouter.back(options)
        }
      }
    } else {
      sysRouter.back()
    }
  }

  /**
   * 后退指定步数并返回实际后退的步数
   * @param steps 要后退的步数
   * @param params 后退到的页面路由参数
   * @param mustBack 不满足期望后退步数时是否仍旧后退
   * @param strictParams 是否仅满足后退步数时传递路由参数
   * @returns 实际后退的步数(当路由栈数量少于期望后退的步数时以实际后退步数为准)
   */
  export function backSteps(steps: number, params?: Object, mustBack?: boolean, strictParams?: boolean): number {
    let len = getCount()
    if (len > 1 && steps > 0) {
      // compute back to index
      let factSteps = Math.min(steps, len)
      let index = len - factSteps
      let satisfy: boolean = factSteps >= steps
      if (mustBack || satisfy) {
        if (params && (satisfy || strictParams !== true)) {
          sysRouter.back(index, params)
        } else {
          sysRouter.back(index)
        }
        return factSteps
      }
    }
    return 0
  }

  /**
   * 以异步谓词决定要后退到的页面
   * @param routePredicate 路由谓词
   * @returns 返回后退了的步数
   */
  export function backTo(routePredicate: RoutePredicate): Promise<number> {
    if (!routePredicate) {
      return new Promise<number>((resolve, _reject) => resolve(0))
    }
    let len = getCount()
    if (len > 1) {
      return new Promise<number>(async (resolve, reject) => {
        let targetIndex = len
        let params: object
        var hasError: boolean = false
        let vas = VarStorage.getInstance()
        // 排除当前页
        for (let index = len - 1; index > 0; index--) {
          let state = getStateByIndex(index)
          let stackPage = vas?.routerStack.find(rp => rp?.index === index)
          let routePageSource = vas?.pageMap.find(pi => pi.pageSourceId === stackPage?.pageSourceId)
          try {
            let result = await routePredicate(state, stackPage, routePageSource)
            if (result.match) {
              targetIndex = index;
              params = result.params
              if (result.continue !== true) {
                break
              }
            }
          } catch (err) {
            hasError = true
            reject(err)
            break
          }
        }
        if (!hasError && targetIndex < len) {
          let steps = len - targetIndex
          if (params) {
            sysRouter.back(targetIndex, params)
          } else {
            sysRouter.back(targetIndex)
          }
          resolve(steps)
        }
      })
    }
    return new Promise<number>((resolve, _reject) => resolve(0))
  }

  /**
   * 以谓词决定要后退到的页面
   * @param routePredicate 路由谓词
   * @returns 返回后退了的步数
   */
  export function backToSync(routePredicate: RoutePredicateSync): number {
    if (!routePredicate) {
      return 0
    }
    let len = getCount()
    if (len > 1) {
      let targetIndex = len
      let params: object
      let vas = VarStorage.getInstance()
      for (let index = len - 1; index > 0; index--) {
        let state = getStateByIndex(index)
        let stackPage = vas?.routerStack.find(rp => rp?.index === index)
        let routePageSource = vas?.pageMap.find(pi => pi.pageSourceId === stackPage?.pageSourceId)
        let result = routePredicate(state, stackPage, routePageSource)
        if (result.match) {
          targetIndex = index;
          params = result.params
          if (result.continue !== true) {
            break
          }
        }
      }
      if (targetIndex < len) {
        let steps = len - targetIndex
        if (params) {
          sysRouter.back(targetIndex, params)
        } else {
          sysRouter.back(targetIndex)
        }
        return steps
      }
    }
    return 0
  }

  export function backThen(routePredicate: RoutePredicate, then: Callback<void>): Promise<number> {
    return new Promise<number>((resolve, reject) => {
      try {
        backTo(routePredicate).then(steps => {
          then && then()
          resolve(steps)
        }).catch(err => {
          reject(err)
        })
      } catch (err) {
        reject(err)
      }
    })
  }

  export function backThenSync(routePredicate: RoutePredicateSync, then: Callback<void>): number {
    let steps = backToSync(routePredicate)
    then && then()
    return steps;
  }

  export function clear(): void {
    sysRouter.clear()
  }

  export function getLength(): string {
    return sysRouter.getLength()
  }

  export function getCount(): number {
    let len = Number(getLength())
    if (!isNaN(len)) {
      return len
    }
    return 1
  }

  export function getStack(): RouterStackPage[] {
    return VarStorage.getInstance()?.routerStack || []
  }

  export function getState(): RouterState {
    return sysRouter.getState()
  }

  export function getStateByIndex(index: number): RouterState | undefined {
    return sysRouter.getStateByIndex(index)
  }

  export function getStateByUrl(url: string): RouterState[] {
    return sysRouter.getStateByUrl(url)
  }

  export function getStateByName(_name: string): RouterState[] {
    return []
  }

  export function getParams(): RouteParams {
    return sysRouter.getParams()
  }

  export function enableAlertBeforeBackPage(options: EnableAlertOptions): void {
    sysRouter.enableAlertBeforeBackPage(options)
  }

  export function showAlertBeforeBackPage(options: EnableAlertOptions): void {
    sysRouter.showAlertBeforeBackPage(options)
  }

  export function disableAlertBeforeBackPage(): void {
    sysRouter.disableAlertBeforeBackPage()
  }

  export function hideAlertBeforeBackPage(): void {
    sysRouter.hideAlertBeforeBackPage()
  }

  export function push(options: RouterOptions | NamedRouterOptions): void {
    if (isRouterOptions(options)) {
      pushUrl(options)
    } else {
      pushNamedRoute(options)
    }
  }

  export function replace(options: RouterOptions | NamedRouterOptions): void {
    if (isRouterOptions(options)) {
      replaceUrl(options)
    } else {
      replaceNamedRoute(options)
    }
  }

  export function pushUrl(options: RouterOptions, mode?: RouterMode | AsyncCallback<void>,
    callback?: AsyncCallback<void>): void | Promise<void> {
    return rOrPUrl(true, options, mode, callback)
  }

  export function replaceUrl(options: RouterOptions, mode?: RouterMode | AsyncCallback<void>,
    callback?: AsyncCallback<void>): void | Promise<void> {
    return rOrPUrl(false, options, mode, callback)
  }

  export function pushNamedRoute(options: NamedRouterOptions, mode?: RouterMode | AsyncCallback<void>,
    callback?: AsyncCallback<void>): void | Promise<void> {
    return rOrPNamed(true, options, mode, callback)
  }

  export function replaceNamedRoute(options: NamedRouterOptions, mode?: RouterMode | AsyncCallback<void>,
    callback?: AsyncCallback<void>): void | Promise<void> {
    return rOrPNamed(false, options, mode, callback)
  }

  /**
   * 获取路由表信息
   * @returns 路由表信息
   */
  export function getRouteMap(): RoutePageSource[] {
    return VarStorage.getInstance().pageMap
  }

  function rOrPUrl(push: boolean, options: RouterOptions, mode?: RouterMode | AsyncCallback<void>,
    callback?: AsyncCallback<void>): void | Promise<void> {
    let typedOptions = normalize(options) as TypedModularOptions
    options = typedOptions as RouterOptions
    let urlOptions: sysRouter.RouterOptions = {
      url: options.url,
      params: options.params
    }
    let sysRouterMode = convertRouterMode(mode)
    let factCallback = mode instanceof Function ? mode as AsyncCallback<void> : callback
    if (push) {
      if (factCallback) {
        ensureImportCall(options.module, typedOptions.pageSrc).finally(() => {
          sysRouter.pushUrl(urlOptions, sysRouterMode, factCallback)
        })
      } else {
        return ensureImportCall(options.module, typedOptions.pageSrc).finally(() => {
          sysRouter.pushUrl(urlOptions, sysRouterMode)
        })
      }
    } else {
      if (factCallback) {
        ensureImportCall(options.module, typedOptions.pageSrc).finally(() => {
          sysRouter.replaceUrl(urlOptions, sysRouterMode, factCallback)
        })
      } else {
        return ensureImportCall(options.module, typedOptions.pageSrc).finally(() => {
          sysRouter.replaceUrl(urlOptions, sysRouterMode)
        })
      }
    }
  }

  function rOrPNamed(push: boolean, options: NamedRouterOptions, mode?: RouterMode | AsyncCallback<void>,
    callback?: AsyncCallback<void>): void | Promise<void> {
    let newOptions = normalize(options)
    let typedOptions = newOptions as TypedModularOptions
    if (isRouterOptions(newOptions)) {
      return rOrPUrl(push, newOptions, mode, callback)
    }
    newOptions = newOptions as NamedRouterOptions
    let namedOptions: sysRouter.NamedRouterOptions = {
      name: newOptions.name,
      params: newOptions.params
    }
    let sysRouterMode = convertRouterMode(mode)
    let factCallback = mode instanceof Function ? mode as AsyncCallback<void> : callback
    if (push) {
      if (factCallback) {
        ensureImportCall(newOptions.module, typedOptions.pageSrc).finally(() => {
          sysRouter.pushNamedRoute(namedOptions, sysRouterMode, factCallback)
        })
      } else {
        return ensureImportCall(newOptions.module, typedOptions.pageSrc).finally(() => {
          sysRouter.pushNamedRoute(namedOptions, sysRouterMode)
        })
      }
    } else {
      if (factCallback) {
        ensureImportCall(newOptions.module, typedOptions.pageSrc).finally(() => {
          sysRouter.replaceNamedRoute(namedOptions, sysRouterMode, factCallback)
        })
      } else {
        return ensureImportCall(newOptions.module, typedOptions.pageSrc).finally(() => {
          sysRouter.replaceNamedRoute(namedOptions, sysRouterMode)
        })
      }
    }
  }

  function ensureImportCall(moduleName?: string, pageSrc?: string): Promise<void> {
    let vas = VarStorage.getInstance()
    let emptyPromise: Promise<void> = new Promise((resolve, _reject) => {
      resolve()
    })
    if (!moduleName || !vas.runtimeOnlyModules.includes(moduleName) || vas.importedModuleCache.includes(moduleName) ||
      moduleName === vas.entryModuleName) {
      // 无需动态import
      return emptyPromise
    } else {
      // 需要想办法消除固定字符串
      let pagePath = `${moduleName}/src/main/ets/${pageSrc}`
      if (vas.importedPageCache.includes(pagePath)) {
        return emptyPromise
      }
      if (vas.routingOptions.ensureImportCall) {
        return vas.routingOptions.ensureImportCall(pagePath, moduleName).then((pagePathOrModuleName: string) => {
          if (pagePathOrModuleName === pagePath) {
            vas.importedPageCache.push(moduleName)
          } else if (pagePathOrModuleName === moduleName) {
            vas.importedModuleCache.push(moduleName)
          }
        })
      }
      // 内置动态import支持
      else {
        return new Promise((resolve, reject) => {
          let importModule = () => {
            // 需要动态import模块
            import(moduleName)
              .then(m => {
                if (m) {
                  vas.importedModuleCache.push(moduleName)
                }
                resolve()
              })
              .catch((err: BusinessError) => {
                reject(err)
              })
          }
          // 先尝试import具体页面
          import(pagePath)
            .then(m => {
              if (m) {
                vas.importedPageCache.push(pagePath)
                resolve()
              } else {
                importModule()
              }
            })
            .catch((_err: BusinessError) => {
              importModule()
            })
        })
      }
    }
  }

  function convertRouterMode(mode: RouterMode | AsyncCallback<void>): sysRouter.RouterMode {
    return mode === RouterMode.Single ? sysRouter.RouterMode.Single : sysRouter.RouterMode.Standard
  }

  function normalize(options: RouterOptions | NamedRouterOptions): RouterOptions | NamedRouterOptions {
    let newOptions = doNormalize(options)
    return newOptions;
  }

  function doNormalize(options: TypedRouterOptions | TypedNamedRouterOptions): TypedRouterOptions | TypedNamedRouterOptions {
    if (!options) {
      return options
    }
    if (options.params?.$rewriteInfo) {
      return options
    }
    tryInit()
    let vs = VarStorage.getInstance()
    if (!vs.context) {
      options.params = options.params || {}
      options.params.$rewriteInfo = options.params.$rewriteInfo || {}
      options.params.$rewriteInfo.rewriteType = RewriteType.None
      options.params.$rewriteInfo.msg = 'context is undefined or router is not initialized'
      return options
    }
    if (isTypedRouterOptions(options)) {
      options.params = options.params || {}
      options.params.$rewriteInfo = options.params.$rewriteInfo || {}
      options.params.$rewriteInfo.url = options.url
      if (!options.url.startsWith(VarStorage.UrlPrefix)) {
        if (vs.urlCache.has(options.url)) {
          let cachedRoute: CachedRoute = vs.urlCache.get(options.url)!
          options.params.$rewriteInfo = cachedRoute.rewriteInfo
          if (options.url !== cachedRoute.value) {
            options.url = cachedRoute.value
          } else {
            options.params.$rewriteInfo.rewriteType = RewriteType.None
          }
        }
        // 未命中缓存
        else {
          // 如果不是统一路径格式
          let page = vs.pageMap.find(pm => pm.src === options.url)
          if (!page && vs.routingOptions.rewriteMode === RewriteMode.Auto) {
            let findUrl = toCamelCase(options.url)
            page = vs.pageMap.find(pm => pm.src === findUrl)
            if (!page) {
              findUrl = toKebabCase(options.url)
              page = vs.pageMap.find(pm => pm.src === findUrl)
            }
          }
          if (page) {
            options.module = page.moduleName
            options.pageSrc = page.src
            options.params.$rewriteInfo.pageSourceId = page.pageSourceId
            if (page.moduleName === vs.entryModuleName) {
              // 在Hap模块下，可以不改写
              options.params.$rewriteInfo.rewriteType = RewriteType.None
              return options
            }
            let fullUrl = `${page.url}`
            let cachedRoute: CachedRoute = {
              rewriteInfo: {},
              value: fullUrl
            }
            options.url = fullUrl
            options.params.$rewriteInfo.rewriteType = RewriteType.Url
            cachedRoute.rewriteInfo = options.params.$rewriteInfo
            vs.urlCache.set(options.url, cachedRoute)
            return options
          } else {
            // 未找到页面配置信息，暂不做处理
            options.params.$rewriteInfo.rewriteType = RewriteType.None
            options.params.$rewriteInfo.msg = `No page configured`
          }
        }
      }
      // 是标准统一的url格式
      else {
        // TODO: 需要检查目标模块类型是否是Har，如果是，需要尝试转为命名路由
      }
    }
    // 命名路由
    else {
      // let namedOptions = options as TypedNamedRouterOptions
      options.params = options.params || {}
      options.params.$rewriteInfo = options.params.$rewriteInfo || {}
      options.params.$rewriteInfo.name = options.name
      if (vs.namedCache.has(options.name)) {
        let cachedRoute: CachedRoute = vs.namedCache.get(options.name)!
        if (cachedRoute.value.startsWith(VarStorage.UrlPrefix)) {
          let pathOptions: TypedRouterOptions = {
            url: cachedRoute.value,
            params: options.params
          }
          pathOptions.params = pathOptions.params || {}
          pathOptions.params.$rewriteInfo = cachedRoute.rewriteInfo || {}
          return pathOptions
        }
        // 非标准统一的url格式
        else {
          options.params.$rewriteInfo = cachedRoute.rewriteInfo || {}
          if (options.name !== cachedRoute.value) {
            options.name = cachedRoute.value
          } else {
            options.params.$rewriteInfo.rewriteType = RewriteType.None
          }
        }
      }
      // 未命中缓存
      else {
        // 匹配路由
        let page =
          vs.pageMap.find(pm => pm.routeName === options.name || pm.alias?.includes(options.name))
        let useRegex = false
        if (!page && vs.routingOptions.rewriteMode === RewriteMode.Auto) {
          let findName = toCamelCase(options.name)
          page = vs.pageMap.find(pm => pm.routeName === findName || pm.alias?.includes(findName))
          if (!page) {
            findName = toKebabCase(options.name)
            page = vs.pageMap.find(pm => pm.routeName === findName || pm.alias?.includes(findName))
          }
          // 如果还未找到，则尝试正则匹配
          if (!page) {
            findName = options.name
            page = vs.pageMap.find(pm => pm.regExp && pm.regExp.test(findName))
            if (page && page.regExp) {
              useRegex = true
              let matches = page.regExp.exec(findName)
              options.params.$rewriteInfo.matches = matches?.filter(x => x && x !== findName)
            }
          }
        }
        if (page) {
          options.module = page.moduleName
          options.pageSrc = page.src
          options.params.$rewriteInfo.pageSourceId = page.pageSourceId
          options.params.$rewriteInfo.regex = useRegex
          let toUrl: boolean = vs.routingOptions.allToUrl ?? true
          if (vs.moduleTypeMap.has(page.moduleName)) {
            toUrl = vs.moduleTypeMap.get(page.moduleName) !== ModuleType.Har
          }
          // TODO: 需要检查目标模块类型是否是Har，如果是，需要尝试转为命名路由
          if (toUrl !== true && page.named && page.routeName) {
            options.params.$rewriteInfo.name = options.name
            let cachedRoute: CachedRoute = {
              rewriteInfo: {},
              value: page.routeName
            }
            if (options.name !== page.routeName) {
              options.name = page.routeName
              options.params.$rewriteInfo.rewriteType = RewriteType.Named
            } else {
              options.params.$rewriteInfo.rewriteType = RewriteType.None
            }
            cachedRoute.rewriteInfo = options.params.$rewriteInfo
            vs.namedCache.set(options.name, cachedRoute)
          }
          // TODO：未命名，需要检查模块类型是否是Har，如果是，则需要尝试通过url匹配命名路由
          else {
            // 尝试访问命名路由，但实际没有命名或配置为全部转Url模式，则转为url访问
            let fullUrl = `${page.url}`
            let cachedRoute: CachedRoute = {
              rewriteInfo: {},
              value: fullUrl
            }
            let pathOptions: TypedRouterOptions = {
              url: fullUrl,
              params: options.params
            }
            pathOptions.module = options.module
            pathOptions.pageSrc = page.src
            pathOptions.params = pathOptions.params || {}
            pathOptions.params.$rewriteInfo = pathOptions.params.$rewriteInfo || {}
            pathOptions.params.$rewriteInfo.name = options.name
            pathOptions.params.$rewriteInfo.rewriteType = RewriteType.NamedToUrl
            cachedRoute.rewriteInfo = pathOptions.params.$rewriteInfo
            vs.namedCache.set(options.name, cachedRoute)
            return pathOptions
          }
        }
        // 未找到页面配置
        else {
          // 未找到页面配置信息，暂不做处理
          options.params.$rewriteInfo.rewriteType = RewriteType.None
          options.params.$rewriteInfo.msg = `No page configured`
        }
      }
    }

    return options
  }

  function tryInit() {
    let vs = VarStorage.getInstance()
    if (!vs.bundleInfo) {
      let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT
        | bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_HAP_MODULE
        | bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_ABILITY;
      let bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleFlags)
      vs.bundleInfo = bundleInfo
      buildRouteMap()
    }
  }

  function buildRouteMap(): void {
    let vs = VarStorage.getInstance()
    // 没有调用初始化，则不构建路由图
    if (!vs.context) {
      return
    }
    if (!vs.entryModuleName) {
      try {
        let metaDataArray = vs.context.resourceManager.getStringArrayByNameSync('compiled_route_meta_data')
        if (metaDataArray?.length) {
          for (let item of metaDataArray) {
            if (item.startsWith(VarStorage.EntryModuleKey)) {
              vs.entryModuleName = item.substring(VarStorage.EntryModuleKey.length)
            } else if (item.startsWith(VarStorage.RuntimeOnlyModuleKey)) {
              vs.runtimeOnlyModules.push(item.substring(VarStorage.RuntimeOnlyModuleKey.length))
            } else if (item.startsWith(VarStorage.ModuleTypeKey)) {
              let value = item.substring(VarStorage.ModuleTypeKey.length)
              let eqIndex = value.lastIndexOf('=')
              let moduleName = value.substring(0, eqIndex)
              let moduleType = value.substring(eqIndex + 1)
              vs.moduleTypeMap.set(moduleName, moduleType)
            }
          }
        }
      } catch (e) {
        hilog.error(0x0000, 'router', '%{public}s',
          `Cannot read [compiled_route_meta_data], cause:${e.message ||
            '<unknown>'}. Please check if the routing compilation plugin [hvigor-easy-router-plugin] is correctly configured`)
      }
    }
    if (vs.pageMap && vs.pageMap.length) {
      return
    }
    try {
      let pageJsonList = vs.context.resourceManager.getStringArrayByNameSync('compiled_route_map')
      if (!pageJsonList || !pageJsonList.length) {
        return
      }
      let idSed: number = 1
      let pages = pageJsonList.map(p => {
        let page = JSON.parse(p) as RoutePageSource
        page.pageSourceId = `${idSed++}`
        if (page.regex) {
          page.regExp = new RegExp(page.regex)
        }
        return page
      })
      vs.pageMap = (vs.pageMap || []).concat(pages || [])
    } catch (e) {
      vs.pageMap = []
      hilog.error(0x0000, 'router', '%{public}s',
        `Cannot read [compiled_route_map], cause:${e.message ||
          '<unknown>'}. Please check if the routing compilation plugin [hvigor-easy-router-plugin] is correctly configured`)
    }
  }

  function isRouterOptions(options: number | ModularOptions): options is RouterOptions {
    if (!options || typeof options !== 'object') {
      return false
    }
    if (Object.keys(options).find(k => k === 'url')) {
      return true
    } else {
      return false;
    }
  }

  function isTypedRouterOptions(options: TypedModularOptions): options is TypedRouterOptions {
    return isRouterOptions(options)
  }

  function toKebabCase(text: string) {
    if (!text) {
      return text;
    }
    text = toCamelCase(text)
    // 正则表达式匹配大写字母，并使用小写字母加短横线进行替换
    return text.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);
  }

  function toCamelCase(text: string) {
    if (!text) {
      return text;
    }
    return text.substring(0, 1).toLowerCase() + text.substring(1);
  }

  function isAbility(target: Object): boolean {
    if (!target || !(target instanceof Function)) {
      return false
    }
    return Object.getPrototypeOf(target)?.name === 'Ability' ||
    target.isPrototypeOf(UIAbility) || target.isPrototypeOf(Ability)
  }

  export enum RouterMode {
    Standard,
    Single
  }

  /**
   * 模块化选项
   */
  export class ModularOptions {
    /**
     * 包名
     */
    bundleName?: string
    /**
     * 模块名
     */
    module?: string
  }

  /**
   * 模块化选项
   */
  class TypedModularOptions extends ModularOptions {
    /**
     * 页面路径
     */
    pageSrc?: string
  }

  /**
   * 路由选项
   */
  export class RoutingOptions {
    /**
     * 重写模式
     */
    rewriteMode?: RewriteMode
    /**
     * 是否全部转换为Url模式跳转
     */
    allToUrl?: boolean
    /**
     * 确保动态引用的调用(如果import成功，则返回pagePath或moduleName)
     */
    ensureImportCall?: (pagePath: string, moduleName: string) => Promise<string>
    /**
     * 指示页面是否被管理
     */
    pageManaged?: (pageInfo: observer.RouterPageInfo) => boolean
  }

  /**
   * 重写类型
   */
  export enum RewriteType {
    /**
     * 未重写
     */
    None = 0,
    /**
     * 重写Url
     */
    Url = 1,
    /**
     * 重写命名
     */
    Named = 2,
    /**
     * 由命名重写为Url
     */
    NamedToUrl = 3
  }

  /**
   * 重写模式
   */
  export enum RewriteMode {
    /**
     * 严格(仅支持定义的名称进行重写)
     */
    Strict = 1,
    /**
     * 自动(智能匹配，支持kebab-case、PascalCase和camelCase格式间相互兼容)
     */
    Auto = 2
  }

  /**
   * 重新路由信息
   */
  export class RewriteRouteInfo {
    /**
     * 页面所在模块
     */
    moduleName?: string
    /**
     * 原Url
     */
    url?: string
    /**
     * 原命名
     */
    name?: string
    /**
     * 重写类型
     */
    rewriteType?: RewriteType
    /**
     * 是否匹配了正则表达式
     */
    regex?: boolean
    /**
     * 正则匹配结果
     */
    matches?: string[]
    /**
     * 重写提示
     */
    msg?: string
    /**
     * 页面源Id
     */
    pageSourceId?: string
  }

  export class RouteParams {
    $rewriteInfo?: RewriteRouteInfo
  }

  export class RouterOptions extends ModularOptions implements sysRouter.RouterOptions {
    url: string = ''
    params ?: object
  }

  export class NamedRouterOptions extends ModularOptions implements sysRouter.NamedRouterOptions {
    name: string = ''
    params?: object
  }

  class TypedRouterOptions extends TypedModularOptions implements sysRouter.RouterOptions {
    url: string = ''
    params ?: RouteParams | undefined
  }

  class TypedNamedRouterOptions extends TypedModularOptions implements sysRouter.NamedRouterOptions {
    name: string = ''
    params?: RouteParams | undefined
  }

  export interface EnableAlertOptions {
    message: string;
  }

  export interface RouterState {
    index: number;
    name: string;
    path: string;
  }

  /**
   * 路由记录选项
   */
  export class RouteOption {
    /**
     * 路由别名
     */
    alias?: Array<string>
    /**
     * 正则表达式(支持正则匹配路由)
     */
    regex?: string
  }

  /**
   * 谓词结果
   */
  export class PredicateResult {
    /**
     * 是否匹配
     */
    match: boolean
    /**
     * 回退页面时的路由参数
     */
    params?: Object
    /**
     * 是否继续匹配
     */
    continue?: boolean
  }

  /**
   * 异步路由谓词
   */
  export interface RoutePredicate {
    /**
     * 判断路由是否匹配要求
     * @param state 路由状态
     * @param stackPage 路由栈页面
     * @param routePageSource 路由页面
     * @returns 谓词结果
     */
    (state: RouterState, stackPage?: RouterStackPage, routePageSource?: RoutePageSource): Promise<PredicateResult>
  }

  /**
   * 路由谓词
   */
  export interface RoutePredicateSync {
    /**
     * 判断路由是否匹配要求
     * @param state 路由状态
     * @param stackPage 路由栈页面
     * @param routePageSource 路由页面
     * @returns 谓词结果
     */
    (state: RouterState, stackPage?: RouterStackPage, routePageSource?: RoutePageSource): PredicateResult
  }

  /**
   * 路由页面
   */
  export class RoutePageSource extends RouteOption {
    /**
     * 页面源Id
     */
    pageSourceId?: string
    /**
     * 模块名称
     */
    moduleName?: string
    /**
     * 页面名称
     */
    name?: string
    /**
     * 路由命名名称
     */
    routeName?: string
    /**
     * 是否命名路由
     */
    named?: boolean
    /**
     * 页面位置(参考main_pages.json中src)
     */
    src?: string
    /**
     * 完整路径
     */
    url?: string
    /**
     * 正则表达式实例
     */
    regExp?: RegExp
  }

  /**
   * 缓存的路由信息
   */
  class CachedRoute {
    /**
     * 重写信息
     */
    rewriteInfo: RewriteRouteInfo
    /**
     * url或命名
     */
    value?: string
  }

  /**
   * 模块类型
   */
  enum ModuleType {
    Entry = 'entry',
    Feature = 'feature',
    Shared = 'shared',
    Har = 'har'
  }

  class VarStorage {
    private static instance: VarStorage;
    /**
     * 统一Url格式前缀(@bundle:)
     */
    public static readonly UrlPrefix: string = '@bundle:'
    public static readonly EntryModuleKey: string = '@entryModule:'
    public static readonly RuntimeOnlyModuleKey: string = '@runtimeOnlyModule:'
    public static readonly ModuleTypeKey: string = '@moduleType:'
    private _context: common.UIAbilityContext
    private _bundleInfo: bundleManager.BundleInfo
    private _routingOptions: RoutingOptions
    private _entryModuleName: string = ''
    private _pageMap: Array<RoutePageSource> = []
    private _runtimeOnlyModules: Array<string> = []
    private _moduleTypeMap: Map<string, string> = new Map()
    private _urlCache: Map<string, CachedRoute> = new Map()
    private _namedCache: Map<string, CachedRoute> = new Map()
    private _importedModuleCache: Array<string> = []
    private _importedPageCache: Array<string> = []
    private _routerPageStack: Map<string, RouterStackPage> = new Map()
    public pageManaged?: (pageInfo: observer.RouterPageInfo) => boolean

    private constructor() {
      // 私有构造函数防止外部使用new创建实例
    }

    public static getInstance(): VarStorage {
      if (!VarStorage.instance) {
        // 确保应用单例
        if (AppStorage.has(RouterStoragePropName)) {
          VarStorage.instance = AppStorage.get<VarStorage>(RouterStoragePropName)
        } else {
          VarStorage.instance = new VarStorage();
          AppStorage.setOrCreate(RouterStoragePropName, VarStorage.instance)
        }
      }
      return VarStorage.instance;
    }

    public static onDestroy() {
      if (VarStorage.instance) {
        observer.off('routerPageUpdate', VarStorage.instance._context, VarStorage.instance.onRouterPageUpdate)
      }
    }

    private initObserver() {
      observer.on('routerPageUpdate', this._context, this.onRouterPageUpdate)
    }

    private onRouterPageUpdate(routerPageInfo: observer.RouterPageInfo) {
      if (!this) {
        let _this: VarStorage = VarStorage.getInstance()
        return _this.onRouterPageUpdate.call(_this, routerPageInfo)
      }
      // 回调判断是否需要处理
      let managed = this.pageManaged?.(routerPageInfo) ?? true
      if (!managed) {
        return
      }
      let page: RouterStackPage
      let key = routerPageInfo.pageId
      if (this._routerPageStack.has(key)) {
        page = this._routerPageStack.get(key)
        page.state = routerPageInfo.state
      } else {
        page = new RouterStackPage(routerPageInfo)
      }
      if (page.isWillShow()) {
        // 页面即将显示，记录页面路由参数
        page.pageSourceId = router.getParams()?.$rewriteInfo?.pageSourceId
      }
      // 判断是否应该移除栈
      if (page.isWillDestroy()) {
        this._routerPageStack.delete(key)
      } else {
        this._routerPageStack.set(key, page)
      }
    }

    get routerStack(): RouterStackPage[] {
      let pages = Array.from(this._routerPageStack.values())
      pages = pages.slice().sort((a, b) => a.index - b.index)
      return pages
    }

    get context(): common.UIAbilityContext {
      return this._context
    }

    set context(context: common.UIAbilityContext) {
      this._context = context
      if (this._context) {
        this.initObserver()
      }
    }

    get bundleInfo(): bundleManager.BundleInfo {
      return this._bundleInfo
    }

    set bundleInfo(bundleInfo: bundleManager.BundleInfo) {
      this._bundleInfo = bundleInfo
    }

    get routingOptions(): RoutingOptions {
      return this._routingOptions
    }

    set routingOptions(routingOptions: RoutingOptions) {
      this._routingOptions = routingOptions
    }

    get entryModuleName(): string {
      return this._entryModuleName;
    }

    set entryModuleName(value: string) {
      this._entryModuleName = value;
    }

    get pageMap(): Array<RoutePageSource> {
      return this._pageMap;
    }

    set pageMap(value: Array<RoutePageSource>) {
      this._pageMap = value;
    }

    get runtimeOnlyModules(): Array<string> {
      return this._runtimeOnlyModules
    }

    get moduleTypeMap(): Map<string, string> {
      return this._moduleTypeMap
    }

    get urlCache(): Map<string, CachedRoute> {
      return this._urlCache;
    }

    set urlCache(value: Map<string, CachedRoute>) {
      this._urlCache = value;
    }

    get namedCache(): Map<string, CachedRoute> {
      return this._namedCache;
    }

    set namedCache(value: Map<string, CachedRoute>) {
      this._namedCache = value;
    }

    get importedModuleCache(): Array<string> {
      return this._importedModuleCache
    }

    get importedPageCache(): Array<string> {
      return this._importedPageCache
    }
  }

  export class RouterStackPage {
    /**
     * The context of the changed router page.
     *
     * @type { UIAbilityContext | UIContext }
     * @syscap SystemCapability.ArkUI.ArkUI.Full
     * @crossplatform
     * @since 11
     */
    /**
     * The context of the changed router page.
     *
     * @type { UIAbilityContext | UIContext }
     * @syscap SystemCapability.ArkUI.ArkUI.Full
     * @crossplatform
     * @atomicservice
     * @since 12
     */
    context: common.UIAbilityContext | UIContext;
    /**
     * The index of the changed router page in router stack.
     *
     * @type { number }
     * @syscap SystemCapability.ArkUI.ArkUI.Full
     * @crossplatform
     * @since 11
     */
    /**
     * The index of the changed router page in router stack.
     *
     * @type { number }
     * @syscap SystemCapability.ArkUI.ArkUI.Full
     * @crossplatform
     * @atomicservice
     * @since 12
     */
    index: number;
    /**
     * The name of the changed router page.
     *
     * @type { string }
     * @syscap SystemCapability.ArkUI.ArkUI.Full
     * @crossplatform
     * @since 11
     */
    /**
     * The name of the changed router page.
     *
     * @type { string }
     * @syscap SystemCapability.ArkUI.ArkUI.Full
     * @crossplatform
     * @atomicservice
     * @since 12
     */
    name: string;
    /**
     * The path of the changed router page.
     *
     * @type { string }
     * @syscap SystemCapability.ArkUI.ArkUI.Full
     * @crossplatform
     * @since 11
     */
    /**
     * The path of the changed router page.
     *
     * @type { string }
     * @syscap SystemCapability.ArkUI.ArkUI.Full
     * @crossplatform
     * @atomicservice
     * @since 12
     */
    path: string;
    /**
     * The state of the changed router page.
     *
     * @type { RouterPageState }
     * @syscap SystemCapability.ArkUI.ArkUI.Full
     * @crossplatform
     * @since 11
     */
    /**
     * The state of the changed router page.
     *
     * @type { RouterPageState }
     * @syscap SystemCapability.ArkUI.ArkUI.Full
     * @crossplatform
     * @atomicservice
     * @since 12
     */
    _state: observer.RouterPageState;
    /**
     * The unique identifier of the router page.
     *
     * @type { string }
     * @syscap SystemCapability.ArkUI.ArkUI.Full
     * @crossplatform
     * @atomicservice
     * @since 12
     */
    pageId: string;
    /**
     * 页面源Id
     */
    pageSourceId?: string
    _stateCode: string
    _stateName: string

    constructor(routerPageInfo: observer.RouterPageInfo) {
      utils.copy(routerPageInfo, this)
    }

    get state(): observer.RouterPageState {
      return this._state
    }

    set state(value: observer.RouterPageState) {
      this._state = value
      this.fillStateCodeAndName()
    }

    get stateCode(): string {
      if (!this._stateCode?.length) {
        this.fillStateCodeAndName()
      }
      return this._stateCode
    }

    get stateName(): string {
      if (!this._stateName?.length) {
        this.fillStateCodeAndName()
      }
      return this._stateName
    }

    isWillShow() {
      return this.state === observer.RouterPageState.ABOUT_TO_APPEAR
    }

    isShow() {
      return this.state === observer.RouterPageState.ON_PAGE_SHOW
    }

    isWillDestroy() {
      return this.state === observer.RouterPageState.ABOUT_TO_DISAPPEAR
    }

    private fillStateCodeAndName() {
      switch (this.state) {
        case observer.RouterPageState.ABOUT_TO_APPEAR:
          this._stateCode = 'ABOUT_TO_APPEAR';
          this._stateName = '即将显示';
          break;
        case observer.RouterPageState.ABOUT_TO_DISAPPEAR:
          this._stateCode = 'ABOUT_TO_DISAPPEAR';
          this._stateName = '即将销毁';
          break;
        case observer.RouterPageState.ON_PAGE_SHOW:
          this._stateCode = 'ON_PAGE_SHOW';
          this._stateName = '显示';
          break;
        case observer.RouterPageState.ON_PAGE_HIDE:
          this._stateCode = 'ON_PAGE_HIDE';
          this._stateName = '隐藏';
          break;
        case observer.RouterPageState.ON_BACK_PRESS:
          this._stateCode = 'ON_BACK_PRESS';
          this._stateName = '返回';
          break;
      }
    }
  }
}

export default router